package com.cskt.config;

import com.cskt.filter.ValidateCodeFilter;
import com.cskt.service.impl.UserDetailServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author admin
 * @date 2023/05/08 11:18
 **/
@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    private static final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);

    @Resource
    private UserDetailServiceImpl userDetailService;
    
    @Resource
    private DataSource dataSource;

    @Resource
    private ObjectMapper objectMapper;
    
    @Resource
    private ValidateCodeFilter validateCodeFilter;

    @Bean
    public WebSecurityCustomizer customizer() {

//        放行静态资源
        return web -> web.ignoring().requestMatchers("/css/**", "js/**", "images/**", "favicon.ico");
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        //禁用伪造跨站请求
        http.csrf().disable();
//        允许加载同源的iframe
        http.headers().frameOptions().sameOrigin();
        
//        添加记住我功能
        http.rememberMe()//开启记住我功能
                .tokenValiditySeconds(3600)// token失效时长，暂设置为一分钟
                .rememberMeParameter("remember-me")//登录页面中，记住我选项的参数名
                .tokenRepository(repository());//配置token持久化仓库
        
//        配置自定义验证码过滤器，在用户名和密码验证过滤器前
        http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class);

        return http.formLogin()// 开启表单认证
                .loginPage("/toLoginPage") // 指定跳转自定义登录页面，与LoginController中的handler搭配使用
                .loginProcessingUrl("/login")//指定自定义登录处理请求路径
                .usernameParameter("username")//指定登录用户名的自定义参数名
                .passwordParameter("password")//指定登录密码的自定义参数名
//                .successForwardUrl("/")//登录成功后跳转 “/” 对应的页面
//                异步登录成功后处理逻辑，封装json格式的响应数据
                .successHandler((request, response, authentication) -> {
                    log.info("当前用户登录成功");
                    Map<String, Object> map = new HashMap<>();
                    map.put("code", 200);
                    map.put("msg", "成功");
                    String stringJson = objectMapper.writeValueAsString(map);
                    response.setContentType("application/json;charset=UTF-8");
                    response.getWriter().println(objectMapper.writeValueAsString(map));
//                    通过objectMapper做为主体，将对象写入流，返回给前端
//                    objectMapper.writeValue(response.getOutputStream(), map);
                    
                })
                //登录失败后的处理--通过lamda表达式，行为参数化
                .failureHandler((request, response, exception) -> {
                    //                    获取登录失败后的异常信息
                    log.warn("登录失败：{}", exception.getMessage());
                    response.sendRedirect("/toLoginPage");
                })
                .and().authorizeHttpRequests().requestMatchers("/toLoginPage","/code/**").permitAll() //放行 "toLoginPage" 
                .anyRequest().authenticated() //表示 所有的请求必须经过认证
                .and().userDetailsService(userDetailService)//通过数据库实现用户认证
                .build();
    }
    
    
    @Bean
    public PersistentTokenRepository repository() {
        JdbcTokenRepositoryImpl repository = new JdbcTokenRepositoryImpl();
//        自定义token持久化配置
        repository.setDataSource(dataSource);//数据源
        repository.setCreateTableOnStartup(false);//用于配置启动时是否在数据库中创建表,第一次启动时设置为true,自动创建表；后续设置为false
        return repository;
    }

}
